package csc411;

import java.text.DecimalFormat;
import java.util.Calendar;

public class Utils {

	public static void printBytes(byte[] b) {
		// just simply for trivial convenience
		for (int i = 0; i < b.length; i++) {
			if (i % 4 == 0) {	// print bytes in columns of 4
				System.out.print("\n");
			}
			System.out.print("[" + b[i] + "] ");

		}
		System.out.print("\n");
	}

	public static byte[] stringToBytes(String str) {
		// the first 4 bytes of the array will contain the string length in int
		// also, the string length must be in multiples of 4

		int length;							// string length (multiples of 4)
		byte[] b;							// the resultant byte array
		byte[] b_length;					// byte array holding string length
		byte[] b_str;						// byte array of string (padded)


		length = Utils.calcStringLength(str.length());

		// initialize all of the byte arrays with the new length
		b = new byte[length + 4];
		b_length = new byte[4];
		b_str = new byte[length];

		// convert string length to byte array
		b_length = Utils.intToBytes(str.length());

		// convert string to byte array
		// note: we cant simply use getBytes() here, we need to do an arraycopy
		// to understand how arraycopy works, see:
		//		http://renaud.waldura.com/doc/java/array-operations.shtml
		System.arraycopy(str.getBytes(), 0, b_str, 0, str.length());

		// pad the b_str array with 0 bits
		for (int i = str.length(); i < length; i++) {
			b_str[i] = 0;
		}

		// now, we simply arraycopy the b_length and b_str to b
		System.arraycopy(b_length, 0, b, 0, 4);
		System.arraycopy(b_str, 0, b, 4, length);

		/*
		System.out.println("str: " + str + " length: " + str.length());
		System.out.print("Length in bytes: ");
		Utils.printBytes(b_length);
		System.out.print("Str in bytes: ");
		Utils.printBytes(b_str);
		System.out.print("Results in bytes: ");
		Utils.printBytes(b);
		 */

		return b;
	}

	public static String bytesToString(byte[] b) {
		String str;							// resultant string
		int length;							// string length
		byte[] b_length;					// byte array holding string length
		byte[] b_str;						// byte array holding the string

		// get the first 4 bytes to find out the string length
		b_length = new byte[4];
		System.arraycopy(b, 0, b_length, 0, 4);

		// get string length
		length = Utils.bytesToInt(b_length);
//		System.out.println("Length: "+length);

		// get the bytes of the string of the intended length
		b_str = new byte[length];
		System.arraycopy(b, 4, b_str, 0, length);

		// simply convert these bytes into a string
		str = new String(b_str);
//		System.out.println("String: "+str);

		return str;
	}

	public static byte[] fixedStringToBytes(String str) {

//		System.out.println("Old: " + str);

		// we need to make sure that the fixed string is exactly 4 chars long
		// (but of course, the client/server program needs to do checks too
		//	before converting to byte array
		if (str.length() > 4) {

			// if the string length is more than 4, we'll truncate it
			str = str.substring(0, 4);

		} else if (str.length() < 4) {

			// if the string length is less than 4, pad it with string zeros
			int currLength = str.length();
			for (int i = currLength; i < 4; i++) {
				str = str + "0";
			}
		}

//		System.out.println("New: " + str);
		byte[] b = new byte[4];


		return str.getBytes();
	}

	public static String bytesToFixedString(byte[] b) {
		byte[] b_4 = new byte[4];

		// ensuring we only get the first 4 bytes
		System.arraycopy(b, 0, b_4, 0, 4);

		return new String(b_4);
	}

	public static byte[] intToBytes(int value) {

		byte[] b = new byte[4];

		for (int i = 0; i < 4; i++) {
			int offset = (b.length - 1 - i) * 8;
			b[i] = (byte) ((value >>> offset) & 0xFF);
		}
		return b;
	}

	public static int bytesToInt(byte[] b) {

		int offset = 0;
		int value = 0;

		for (int i = 0; i < 4; i++) {
			int shift = (4 - 1 - i) * 8;
			value += (b[i + offset] & 0x000000FF) << shift;
		}
		return value;
	}

	// Nadiah (230210:2345)
	public static byte[] floatToBytes(float value) {
		int i = Float.floatToRawIntBits(value);

		return Utils.intToBytes(i);
	}

	// Nadiah (230210:2350)
	public static float bytesToFloat(byte[] b) {
		int bits = 0;
		int i = 0;
		for (int shift = 3; shift >= 0; shift--) {
			bits |= ((int) b[i] & 0xff) << (shift * 8);
			i++;
		}

		return Float.intBitsToFloat(bits);
	}

	// Nadiah (240210:1220)
	public static byte[] currencyToByte(Currency type) {
		// use type.ordinal() to convert the currency to its order number (int)
		// next, convert that int to byte array.
		int i = type.ordinal();

		return Utils.intToBytes(i);
	}

	// Nadiah (240210:1225) - To Revisit
	public static Currency byteToCurrency(byte[] b) {

		// convert the byte array to int (which will be its currency ordinal)
		// then, use type.values()[ordinal] to get the currency value.
		int i = Utils.bytesToInt(b);

		//Currency curr;
		int count = 0;
		for (Currency c : Currency.values()) {
			if (i == count) {
				return c;
			} else {
				count++;
			}
		}
		return Currency.USD;
	}

	//uncomment if need to use
   	/*public static byte[] booleanToBytes(boolean value) {

	int bl;

	//if true, initialise to 1
	if (value)
	{
	bl = 1;
	}
	else
	{
	bl = 0;
	}
	return Utils.intToBytes(bl);
	}

	//uncomment if need to use
	public static boolean bytesToBoolean(byte[] b) {
	// convert the byte array to boolean
	int bl = Utils.bytesToInt(b);

	if (bl == 1)
	{
	return true;
	}
	else
	{
	return false;
	}
	}*/
	public static void clearBit(byte[] b, int pos) {
		setBit(b, pos, 0);
	}

	public static void setBit(byte[] b, int pos, int val) {
		// http://www.herongyang.com/java/Bit-String-Set-Bit-to-Byte-Array.html
		// with modifications on posByte and posBit
		int posByte = b.length - 1 - pos / 8;
		int posBit = 8 - 1 - pos % 8;

		byte oldByte = b[posByte];
		oldByte = (byte) (((0xFF7F >> posBit) & oldByte) & 0x00FF);

		byte newByte = (byte) ((val << (8 - (posBit + 1))) | oldByte);
		b[posByte] = newByte;

	}

	public static int getBit(byte[] b, int pos) {
		//http://www.herongyang.com/java/Bit-String-Get-Bit-from-Byte-Array.html
		// with modifications on posByte and posBit
		int posByte = b.length - 1 - pos / 8;
		int posBit = 8 - 1 - pos % 8;

		byte valByte = b[posByte];

		int valInt = valByte >> (8 - (posBit + 1)) & 0x0001;

		return valInt;
	}

	// return the length in multiple of 4
	// eg	input 5, output 8
	//		input 3, output 4
	public static int calcStringLength(int length) {
		if (length % 4 != 0) {
			length = length + (4 - length % 4);
		}
		return length;
	}

	public static String printMoney(Currency type, float amount) {

		String value = "";
		DecimalFormat d = new DecimalFormat("0.##");
		value = type.toString() + "" + d.format(amount);

		return value;

	}

	public static boolean isDroppedPacket(double packetLossRate) {

		boolean isPacketLost = false;

		double random = Math.random();
		System.out.println("[NETWORK] Packet loss % = " + packetLossRate + " / Random: " + random);
		if (random < 1 - packetLossRate) {
			isPacketLost = false;
		} else {
			isPacketLost = true;
		}

		return isPacketLost;
	}

	public static String getTime() {
		String result = "";

		if (Calendar.getInstance().get(Calendar.HOUR_OF_DAY) < 9) {
			result += "0" + Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
		} else {
			result += "" + Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
		}

		if (Calendar.getInstance().get(Calendar.MINUTE) < 9) {
			result += ":0" + Calendar.getInstance().get(Calendar.MINUTE);
		} else {
			result += ":" + Calendar.getInstance().get(Calendar.MINUTE);
		}


		if (Calendar.getInstance().get(Calendar.SECOND) < 9) {
			result += ":0" + Calendar.getInstance().get(Calendar.SECOND);
		} else {
			result += ":" + Calendar.getInstance().get(Calendar.SECOND);
		}

		if (Calendar.getInstance().get(Calendar.AM_PM) == 0) {
			result += "AM";
		} else {
			result += "PM";
		}

		return result;
	}
}
